//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.core
{


	using GraphDatabaseService = org.neo4j.graphdb.GraphDatabaseService;
	using Node = org.neo4j.graphdb.Node;
	using Relationship = org.neo4j.graphdb.Relationship;
	using RelationshipType = org.neo4j.graphdb.RelationshipType;
	using Transaction = org.neo4j.graphdb.Transaction;
	using EmbeddedGraphDatabase = org.neo4j.kernel.EmbeddedGraphDatabase;
	using AbstractNeo4jTestCase = org.neo4j.kernel.impl.AbstractNeo4jTestCase;
	using MyRelTypes = org.neo4j.kernel.impl.MyRelTypes;
	using NeoModule = org.neo4j.kernel.impl.core.NeoModule;
	using NodeManager = org.neo4j.kernel.impl.core.NodeManager;

	public class TestNeo4j : AbstractNeo4jTestCase
	{
		public TestNeo4j(string testName) : base(testName)
		{
		}

		public virtual void testReferenceNode()
		{
		// fix this test when we can set reference node again
			Node oldReferenceNode = null;
			try
			{
			// get old reference node if one is set
				oldReferenceNode = getGraphDb().getReferenceNode();
			}
			catch (RuntimeException e)
			{
			// ok no one set, oldReferenceNode is null then
			}
			try
			{
				NeoModule neoModule = ((EmbeddedGraphDatabase) getGraphDb()).getConfig().getNeoModule();

				Node newReferenceNode = getGraphDb().createNode();
				neoModule.setReferenceNodeId((int) newReferenceNode.getId());
				assertEquals(newReferenceNode, getGraphDb().getReferenceNode());
				newReferenceNode.delete();
				if (oldReferenceNode != null)
				{
					neoModule.setReferenceNodeId((int) oldReferenceNode.getId());
					assertEquals(oldReferenceNode, getGraphDb().getReferenceNode());
				}
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}

		public virtual void testBasicNodeRelationships()
		{
			Node firstNode = null;
			Node secondNode = null;
			Relationship rel = null;
		// Create nodes and a relationship between them
			firstNode = getGraphDb().createNode();
			assertNotNull("Failure creating first node", firstNode);
			secondNode = getGraphDb().createNode();
			assertNotNull("Failure creating second node", secondNode);
			rel = firstNode.createRelationshipTo(secondNode, MyRelTypes.TEST);
			assertNotNull("Relationship is null", rel);
			RelationshipType relType = rel.getType();
			assertNotNull("Relationship's type is is null", relType);

		// Verify that the node reports that it has a relationship of
		// the type we created above
			assertTrue(firstNode.getRelationships(relType).GetEnumerator().MoveNext());
			assertTrue(secondNode.getRelationships(relType).GetEnumerator().MoveNext());

			Iterable<Relationship> allRels = null;

		// Verify that both nodes return the relationship we created above
			allRels = firstNode.getRelationships();
			assertTrue(this.objectExistsInIterable(rel, allRels));
			allRels = firstNode.getRelationships(relType);
			assertTrue(this.objectExistsInIterable(rel, allRels));

			allRels = secondNode.getRelationships();
			assertTrue(this.objectExistsInIterable(rel, allRels));
			allRels = secondNode.getRelationships(relType);
			assertTrue(this.objectExistsInIterable(rel, allRels));

		// Verify that the relationship reports that it is associated with
		// firstNode and secondNode
			Node[] relNodes = rel.getNodes();
			assertEquals("A relationship should always be connected to exactly " + "two nodes", relNodes.Length, 2);
			assertTrue("Relationship says that it isn't connected to firstNode", this.objectExistsInArray(firstNode, relNodes));
			assertTrue("Relationship says that it isn't connected to secondNode", this.objectExistsInArray(secondNode, relNodes));
			assertTrue("The other node should be secondNode but it isn't", rel.getOtherNode(firstNode).Equals(secondNode));
			assertTrue("The other node should be firstNode but it isn't", rel.getOtherNode(secondNode).Equals(firstNode));
			rel.delete();
			secondNode.delete();
			firstNode.delete();
		}

		private bool objectExistsInIterable(Relationship rel, Iterable<Relationship> allRels)
		{
			foreach (Relationship iteratedRel in allRels)
			{
				if (rel.Equals(iteratedRel))
				{
					return true;
				}
			}
			return false;
		}

		private bool objectExistsInArray(object obj, object[] objArray)
		{
			for (int i = 0; i < objArray.Length; i++)
			{
				if (objArray[i].Equals(obj))
				{
					return true;
				}
			}
			return false;
		}

//JAVA TO VB & C# CONVERTER TODO TASK: Enums cannot implement interfaces in .NET:
//ORIGINAL LINE: private static enum RelTypes implements RelationshipType
		private static enum RelTypes
		{
			ONE_MORE_RELATIONSHIP
		}

	// TODO: fix this testcase
		public virtual void testIdUsageInfo()
		{
			NeoModule neoModule = ((EmbeddedGraphDatabase) getGraphDb()).getConfig().getNeoModule();
			NodeManager nm = neoModule.getNodeManager();
			long nodeCount = nm.getNumberOfIdsInUse(typeof(Node));
			long relCount = nm.getNumberOfIdsInUse(typeof(Relationship));
			if (nodeCount > nm.getHighestPossibleIdInUse(typeof(Node)))
			{
			// fail( "Node count greater than highest id " + nodeCount );
			}
			if (relCount > nm.getHighestPossibleIdInUse(typeof(Relationship)))
			{
			// fail( "Rel count greater than highest id " + relCount );
			}
		// assertTrue( nodeCount <= nm.getHighestPossibleIdInUse( Node.class )
		// );
		// assertTrue( relCount <= nm.getHighestPossibleIdInUse(
		// Relationship.class ) );
			Node n1 = nm.createNode();
			Node n2 = nm.createNode();
			Relationship r1 = n1.createRelationshipTo(n2, MyRelTypes.TEST);
		// assertEquals( nodeCount + 2, nm.getNumberOfIdsInUse( Node.class ) );
		// assertEquals( relCount + 1, nm.getNumberOfIdsInUse(
		// Relationship.class ) );
			r1.delete();
			n1.delete();
			n2.delete();
		// must commit for ids to be reused
			try
			{
				getTransaction().success();
				getTransaction().finish();
			}
			catch (Exception e)
			{
				fail("" + e);
			}
		// assertEquals( nodeCount, nm.getNumberOfIdsInUse( Node.class ) );
		// assertEquals( relCount, nm.getNumberOfIdsInUse( Relationship.class )
		// );
			setTransaction(getGraphDb().beginTx());
		}

		public virtual void testRandomPropertyName()
		{
			Node node1 = getGraphDb().createNode();
			string key = "random_" + new Random(System.currentTimeMillis()).nextLong();
			node1.setProperty(key, "value");
			assertEquals("value", node1.getProperty(key));
			node1.delete();
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void testNodeChangePropertyArray() throws Exception
		public virtual void testNodeChangePropertyArray()
		{
			Transaction tx = getTransaction();
			tx.finish();
			tx = getGraphDb().beginTx();
			Node node;
			try
			{
				node = getGraphDb().createNode();
				tx.success();
			}
			finally
			{
				tx.finish();
			}
			tx = getGraphDb().beginTx();
			try
			{
				node.setProperty("test", new string[] { "value1" });
				tx.success();
			}
			finally
			{
				tx.finish();
			}
			tx = getGraphDb().beginTx();
			try
			{
				node.setProperty("test", new string[] { "value1", "value2" });
			// no success, we wanna test rollback on this operation
			}
			finally
			{
				tx.finish();
			}
			tx = getGraphDb().beginTx();
			try
			{
				string[] @value = (string[]) node.getProperty("test");
				assertEquals(1, @value.Length);
				assertEquals("value1", @value[0]);
				tx.success();
			}
			finally
			{
				tx.finish();
			}
			setTransaction(getGraphDb().beginTx());
		}

		public virtual void testMultipleNeos()
		{
			GraphDatabaseService neo2 = new EmbeddedGraphDatabase(getStorePath("test-neo2"));
			Transaction tx2 = neo2.beginTx();
			getGraphDb().createNode();
			neo2.createNode();
			tx2.success();
			tx2.finish();
			neo2.shutdown();
		}

		public virtual void testGetAllNode()
		{
			long highId = getNodeManager().getHighestPossibleIdInUse(typeof(Node));
			if (highId >= 0 && highId < 10000)
			{
				int count = 0;
				foreach (Node node in getEmbeddedGraphDb().getAllNodes())
				{
					count++;
				}
				bool found = false;
				Node newNode = getGraphDb().createNode();
				newTransaction();
				int oldCount = count;
				count = 0;
				foreach (Node node in getEmbeddedGraphDb().getAllNodes())
				{
					count++;
					if (node.Equals(newNode))
					{
						found = true;
					}
				}
				assertTrue(found);
				assertEquals(count, oldCount + 1);

			// Tests a bug in the "all nodes" iterator
				Iterator<Node> allNodesIterator = getEmbeddedGraphDb().getAllNodes().GetEnumerator();
				assertNotNull(allNodesIterator.Current);

				newNode.delete();
				newTransaction();
				found = false;
				count = 0;
				foreach (Node node in getEmbeddedGraphDb().getAllNodes())
				{
					count++;
					if (node.Equals(newNode))
					{
						found = true;
					}
				}
				assertTrue(!found);
				assertEquals(count, oldCount);
			}
		// else we skip test, takes too long
		}
	}
}